﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JigLibX.Physics;
using JigLibX.Collision;
using JigLibX.Geometry;
using Microsoft.Xna.Framework;
using JigLibX.Math;
using Microsoft.Xna.Framework.Graphics;
using System.Xml;
using PloobsEngine.Utils;
using PloobsEngine.Modelo;

namespace PloobsEngine.Physics.XJigLib
{
    public delegate bool handleCollisionDetection(CollisionSkin owner, CollisionSkin collidee);

    public enum CollisionCallBackOptions
    {
        TRIANGLE_MESH_AND_IMMOBILE
    }

    public class CharacterObject : JigLibXObject
    {
        Matrix cor = Matrix.Identity;
        float rotation = 0;
        float maxy = float.MinValue;
        float miny = float.MaxValue;
        Vector2 maxRaio = new Vector2(0);
        float mass = 60;
        Matrix modelOrientationfix = Matrix.Identity;

        CollidesDelegation cd = new CollidesDelegation();
        public CollisionCallBackOptions HandleCollisionDetection
        {            
            set {
                switch (value)  
                {
                    case CollisionCallBackOptions.TRIANGLE_MESH_AND_IMMOBILE:
                        CharacterBody.CollisionSkin.callbackFn += new CollisionCallbackFn(cd.TRIANGLE_MESH_AND_IMMOBILE);
                        break;
                    default:
                        break;
                }
            }
        }

        float raio;
        float comp;
        Vector3 fix = Vector3.Zero;

        public float RotationAngle
        {
            get
            {
                return rotation;
            }
            set
            {
                this.rotation = value;
            }
        }

        public override Vector3 FaceVector
        {
            get
            {
                return Vector3.Transform(Vector3.UnitZ, Rotation * Matrix.Invert(cor));                
            }
        }

        public override Matrix Rotation
        {
            get
            {
                return  cor * Matrix.CreateRotationY(MathHelper.ToRadians(RotationAngle)) * Body_.Orientation;
                //return base.Rotation;
            }
            set
            {
                base.Rotation = value;
            }
        }

        public Character CharacterBody { get; set; }

        //private void GetCapsuleFromModel(ModelMesh[] model)
        //{
        //    float Xmaxy = maxy;
        //    float Xminy = miny;
        //    Vector2 XmaxRaio = maxRaio;

        //    foreach (ModelMesh mesh in model)
        //    {
        //        VertexPositionNormalTexture[] vertices =
        //          new VertexPositionNormalTexture[mesh.VertexBuffer.SizeInBytes / VertexPositionNormalTexture.SizeInBytes];

        //        mesh.VertexBuffer.GetData<VertexPositionNormalTexture>(vertices);

        //        Vector3[] vertexs = new Vector3[vertices.Length];

        //        maxy = miny = vertices[0].Position.Y;

        //        for (int index = 0; index < vertexs.Length; index++)
        //        {
        //            vertexs[index] = vertices[index].Position;
        //            if (vertexs[index].Y > maxy)
        //            {
        //                maxy = vertexs[index].Y;
        //            }
        //            if (vertexs[index].Y < miny)
        //            {
        //                miny = vertexs[index].Y;
        //            }
        //            Vector2 v = new Vector2(vertexs[index].X, vertexs[index].Z);
        //            if (v.Length() > maxRaio.Length())
        //            {
        //                maxRaio = v;
        //            }

        //        }

        //        if (XmaxRaio.Length() < maxRaio.Length())
        //            XmaxRaio = maxRaio;
        //        if (Xminy > miny)
        //            Xminy = miny;
        //        if (Xmaxy < maxy)
        //            Xmaxy = maxy;
        //    }

        //    maxRaio = XmaxRaio;
        //    miny = Xminy;
        //    maxy = Xmaxy;

        //}

        public CharacterObject()
        {
        }

        //public CharacterObject(IModelo modelo, Vector3 position)
        //{

        //    GetCapsuleFromModel(modelo.getModelMeshes());

        //    raio = maxRaio.Length();
        //    comp = maxy - miny;
        //    //Init(position, raio, comp, Vector3.Zero, Matrix.Identity);

        //    Body = new Character();
        //    CollisionSkin = new CollisionSkin(Body);


        //    Capsule capsule = new Capsule(Vector3.Zero, Matrix.Identity, raio, comp);            
        //    CollisionSkin.AddPrimitive(capsule, (int)MaterialTable.MaterialID.NotBouncyNormal);
        //    Body.CollisionSkin = this.CollisionSkin;
        //    Vector3 com = SetMass(mass);

        //    Body.MoveTo(position + com, Matrix.Identity);
        //    CollisionSkin.ApplyLocalTransform(new Transform(-com, Matrix.Identity));

        //    Body.SetBodyInvInertia(0.0f, 0.0f, 0.0f);

        //    Body.AllowFreezing = false;
        //    Body.DisableBody();
        //    CharacterBody = Body as Character;

        //}

        //public CharacterObject(IModelo model , Vector3 position,Vector3 posCorrection, Matrix rotCorrection, float modelYRotation)
        //    : this(model, position)
        //{
        //    fix = posCorrection;
        //    cor = Matrix.CreateRotationY(MathHelper.ToRadians(modelYRotation));
        //    CollisionSkin.ApplyLocalTransform(new Transform(posCorrection, rotCorrection));
        //}

        public float ModelYOrientationCorrection
        {
            set
            {
                cor = Matrix.CreateRotationY(MathHelper.ToRadians(value));
            }
        }

        public CharacterObject(Vector3 position, float raio, float comp, Vector3 posCorrection, Matrix rotCorrection)
            : this(position, raio, comp)
        {
            fix = posCorrection;
            CollisionSkin.ApplyLocalTransform(new Transform(posCorrection, rotCorrection));
        }
        public CharacterObject(Vector3 position, float raio, float comp)
        {
            this.raio = raio;
            this.comp = comp;
            Init(position, raio, comp, Vector3.Zero, Matrix.Identity);
        }

        public CharacterObject(Vector3 position, float raio, float comp,Vector3 scale)
        {
            this.scale = scale;
            this.raio = raio * new Vector2(scale.X,scale.Z).Length();
            this.comp = comp * scale.Y;
            Init(position, raio, comp, Vector3.Zero, Matrix.Identity);
        }

        private void Init(Vector3 position, float raio, float comp, Vector3 posCorrection, Matrix rotCorrection)
        {
            Body = new Character();
            CollisionSkin = new CollisionSkin(Body);

            Capsule capsule = new Capsule(Vector3.Zero, Matrix.CreateRotationX(MathHelper.PiOver2) , raio, comp);
            CollisionSkin.AddPrimitive(capsule, (int)MaterialTable.MaterialID.NotBouncyNormal);
            Body.CollisionSkin = this.CollisionSkin;
            Vector3 com = SetMass(mass);

            Body.MoveTo(position + com, Matrix.Identity);
            CollisionSkin.ApplyLocalTransform(new Transform(-com, Matrix.Identity));
            Body.SetBodyInvInertia(0.0f, 0.0f, 0.0f);
            CharacterBody = Body as Character;

            Body.AllowFreezing = false;  
            CollisionSkin.ApplyLocalTransform(new Transform(posCorrection, rotCorrection));
        }

        private bool handleCollisionDetection(CollisionSkin owner, CollisionSkin collidee)
        {
            return handleCollisionDetection(owner, collidee);
        }
        
        public override PhysicObjectTypes PhysicObjectTypes
        {
            get { return PhysicObjectTypes.CHARACTEROBJECT; }
        }

        public override void DeSerialize(XmlNode node, EngineStuff engine, IModelo model, Vector3 position, Matrix rotation)
    {

            this.raio = SerializerHelper.DeserializeBaseType<float>("Raio", node);
            this.comp = SerializerHelper.DeserializeBaseType<float>("Comprimento", node);
            this.fix = SerializerHelper.DeserializeVector3("Deslocamento", node);
            Init(position, raio, comp, fix, rotation);
            this.isMotionLess = !SerializerHelper.DeserializeBaseType<bool>("CanMove", node);

        }

        public override void Serialize(System.Xml.XmlTextWriter textWriter)
        {
            SerializerHelper.SerializeBaseType<bool>(isMotionLess, "CanMove", textWriter);
            SerializerHelper.SerializeBaseType<float>(raio, "Raio", textWriter);
            SerializerHelper.SerializeBaseType<float>(comp,"Comprimento",textWriter);
            SerializerHelper.SerializeVector3(fix,"Deslocamento", textWriter);            

        }
        
        class ASkinPredicate : CollisionSkinPredicate1
        {
            public override bool ConsiderSkin(CollisionSkin skin0)
            {
                if (!(skin0.Owner is Character))
                    return true;
                else
                    return false;
            }
        }

        public class Character : Body
        {

            public Character()
                : base()
            {
            }

            public Vector3 DesiredVelocity { get; set; }

            private bool doJump = false;

            public void DoJump()
            {
                doJump = true;
            }


            public override void AddExternalForces(float dt)
            {
                ClearForces();

                if (doJump)
                {
                    foreach (CollisionInfo info in CollisionSkin.Collisions)
                    {
                        Vector3 N = info.DirToBody0;
                        if (this == info.SkinInfo.Skin1.Owner)
                            Vector3.Negate(ref N, out N);

                        if (Vector3.Dot(N, Orientation.Up) > 0.7f)
                        {
                            Vector3 vel = Velocity;
                            vel.Y = 15.0f;
                            Velocity = vel;
                            break;
                        }
                    }
                }

                Vector3 deltaVel = DesiredVelocity - Velocity;

                bool running = true;

                if (DesiredVelocity.LengthSquared() < JiggleMath.Epsilon) running = false;
                else deltaVel.Normalize();

                deltaVel.Y = 0.0f;

                // start fast, slow down slower
                if (running) deltaVel *= 10.0f;
                else deltaVel *= 2.0f;

                float forceFactor = 1000.0f;

                AddBodyForce(deltaVel * Mass * dt * forceFactor);

                doJump = false;
                AddGravityToExternalForce();
            }
        }

    }
}
